package com.pokermind.model.po;

import java.math.BigDecimal;

/**
 * 扑克手牌行动实体类 - 德州扑克核心数据模型
 *
 * 对应 poker_hand_actions 表，记录德州扑克游戏中每一个玩家行动的完整信息 用于游戏重建、行为分析、统计计算等核心功能
 *
 * 设计原则： - 完整记录：捕获行动的所有关键信息 - 时序准确：精确记录行动的时间和序列 - 业务友好：提供德扑专业的业务方法
 *
 * @author PokerMind Team
 */
public class PokerHandAction {

    /**
     * 主键ID - 数据库自增主键
     *
     * 业务含义： - 唯一标识每一个行动记录 - 用于数据关联和引用 - 保证行动记录的唯一性
     *
     * 数据约束：Long类型，自动生成，不可重复
     */
    private Long id;

    /**
     * 关联手牌ID - 行动所属手牌的标识
     *
     * 业务含义： - 将行动关联到具体的手牌 - 用于查询某手牌的所有行动历史 - 支持手牌级别的数据分析 - 与enhanced_poker_hand表的关联外键
     *
     * 数据约束：Long类型，必须存在对应的手牌记录
     */
    private Long handId;

    /**
     * 行动玩家座位号 - 德扑桌上的物理位置
     *
     * 业务含义： - 标识执行行动的玩家在桌上的座位位置 - 用于确定行动顺序和位置策略分析 - 与庄家按钮位置结合计算相对位置优势 - 支持位置统计(UTG、CO、BTN等位置的行动模式)
     *
     * 业务规则： - 通常范围：1-10（支持2人到10人桌） - 与手牌中的玩家座位信息保持一致 - 用于验证行动顺序的合法性
     *
     * 数据约束：Integer类型，1-10范围
     */
    private Integer seatNumber;

    /**
     * 前端玩家ID - 业务标识符
     *
     * 业务含义： - 前端系统提供的玩家标识（如: hero_001, mp_player_004） - 用于业务逻辑和前后端数据同步 - 具有明确的业务语义，便于调试和日志追踪 -
     * 与ActionRequest中的playerId直接对应
     *
     * 业务价值： - 直接对应前端传递的玩家标识 - 无需复杂的转换和查询逻辑 - 保持业务语义的清晰性 - 简化数据流处理
     *
     * 数据约束：String类型，不能为空，与前端保持一致
     */
    private String playerId;

    /**
     * 数据库用户ID - 高效查询标识符
     *
     * 业务含义： - 关联poker_users表的自增主键ID - 用于高性能的用户行为统计和分析 - 支持复杂的SQL查询和JOIN操作 - 避免字符串比较的性能开销
     *
     * 性能优势： - 数字类型比较比字符串快约10倍 - 支持高效的索引和关联查询 - 减少行为分析时的查询复杂度 - 适合大数据量的统计计算
     *
     * 数据关系： - 外键：关联poker_users.id - 一对多：一个用户可以有多个行动记录 - 用于用户级别的统计分析
     *
     * 数据约束：Long类型，必须存在对应的用户记录
     */
    private Long userId;

    /**
     * 街道 - 德州扑克游戏阶段标识
     *
     * 业务含义： - 标识行动发生在哪个游戏阶段 - 不同街道的行动具有不同的战略意义 - 用于分析玩家在不同阶段的行为模式 - 支持街道级别的统计和策略分析
     *
     * 德州扑克街道详解： - "preflop": 翻前阶段，只有底牌，最激进的下注阶段 - "flop": 翻牌阶段，公共牌3张，信息大量增加 - "turn": 转牌阶段，公共牌4张，接近决策关键点 - "river":
     * 河牌阶段，公共牌5张，最终决策阶段
     *
     * 分析价值： - 翻前激进度：反映玩家对起手牌的选择标准 - 翻后连续性：反映玩家的一致性和欺骗性 - 河牌决策：反映玩家的风险控制和价值判断
     *
     * 数据约束：字符串枚举，必须是上述4个值之一
     */
    private String street;

    /**
     * 该街道内序号 - 当前街道内的行动顺序
     *
     * 业务含义： - 标识在当前街道内的行动顺序(1, 2, 3, ...) - 用于重建街道内的行动时序 - 分析玩家在不同行动位置的决策差异 - 支持精确的游戏过程重现
     *
     * 业务规则： - 每个街道从1开始重新计数 - 连续递增，不允许跳跃 - fold行动后该玩家不再有后续序号
     *
     * 分析应用： - 首次行动 vs 响应行动的策略差异 - 多轮加注中的位置价值分析 - 行动压力对决策的影响研究
     *
     * 数据约束：Integer类型，从1开始的正整数
     */
    private Integer actionSequence;

    /**
     * 整手牌序号 - 全局行动顺序标识
     *
     * 业务含义： - 标识该行动在整个手牌中的绝对顺序 - 用于完整重建手牌的时间线 - 支持跨街道的行动序列分析 - 计算手牌的总体节奏和复杂度
     *
     * 业务价值： - 手牌复杂度：总行动数反映手牌的激烈程度 - 决策点分布：分析关键决策在手牌中的位置 - 节奏控制：分析玩家对手牌节奏的影响
     *
     * 计算规则： - 从1开始，整个手牌连续递增 - 跨越所有街道，保持唯一性 - 用于确定行动的绝对时序关系
     *
     * 数据约束：Integer类型，从1开始的正整数，手牌内唯一
     */
    private Integer globalSequence;

    /**
     * 行动类型 - 德州扑克中的具体行动
     *
     * 业务含义： - 玩家执行的具体行动类型 - 决定后续游戏流程和底池变化 - 影响其他玩家的可选行动 - 反映玩家的策略意图和手牌强度
     *
     * 德州扑克行动类型详解：
     *
     * 1. "fold" (弃牌)： - 放弃当前手牌，退出本轮游戏 - 损失已投入的筹码，不参与后续行动 - 最保守的行动，风险为零但放弃获胜机会
     *
     * 2. "check" (过牌)： - 不下注但保持在游戏中，将行动权传递 - 只能在无需跟注时执行（没有人下注或已跟注） - 保守行动，保持参与但不增加投入
     *
     * 3. "call" (跟注)： - 跟上当前需要的下注金额 - 被动行动，不主动增加底池大小 - 表示有一定手牌强度但不足以主动加注
     *
     * 4. "bet" (下注)： - 在无人下注时主动投入筹码 - 主动行动，显示手牌强度或进行欺骗 - 用于价值下注或偷盲
     *
     * 5. "raise" (加注)： - 在有人下注基础上增加投注 - 最激进的行动，显示强手牌或强烈欺骗意图 - 增加底池大小，给对手施加压力
     *
     * 6. "allin" (全进)： - 投入所有剩余筹码 - 极端行动，要么极强手牌要么完全欺骗 - 结束该玩家在本手牌中的进一步行动
     *
     * 行动分类： - 主动行动：bet, raise, allin (显示手牌强度) - 被动行动：call, check (相对保守) - 退出行动：fold (完全放弃)
     *
     * 数据约束：字符串枚举，必须是以上6种类型之一
     */
    private String actionType;

    /**
     * 行动金额 - 本次行动涉及的筹码数量
     *
     * 业务含义： - 玩家在本次行动中实际投入的筹码金额 - 反映行动的经济影响和策略意图 - 用于计算底池变化和玩家投入 - 影响后续玩家的决策和可选行动
     *
     * 不同行动类型的金额含义：
     *
     * 1. call (跟注)： - 跟注需要的金额 = 当前最高下注 - 玩家已投入金额 - 用于计算跟注比例和底池赔率 - 反映玩家愿意承担的风险
     *
     * 2. bet (下注)： - 玩家主动投入的金额 - 常见下注比例：1/3底池、1/2底池、3/4底池、满底池 - 反映价值下注还是偷盲意图
     *
     * 3. raise (加注)： - 在原有下注基础上的增加金额 - 最小加注 = 前一个下注/加注的金额 - 反映手牌强度和施压策略
     *
     * 4. allin (全进)： - 玩家所有剩余筹码 - 金额 = 行动前筹码总数 - 极端情况，表示最大承诺或绝望一搏
     *
     * 5. fold/check： - 通常为0或null，因为不涉及筹码投入 - 但记录便于统计和验证
     *
     * 业务规则： - bet/raise/call/allin 必须有正数金额 - fold/check 金额应为0或null - 不能超过玩家当前筹码数 - 必须满足最小下注/加注限制
     *
     * 分析价值： - 下注模式：分析玩家的下注习惯和大小 - 风险承担：通过下注金额评估风险偏好 - 价值提取：分析玩家的价值下注效率
     *
     * 数据约束：BigDecimal类型，精确到小数点后2位，非负数
     */
    private BigDecimal amount;

    /**
     * 行动后筹码 - 玩家执行行动后的剩余筹码
     *
     * 业务含义： - 玩家执行当前行动后的筹码余额 - 用于计算玩家的净投入和损失 - 验证行动合法性的重要指标 - Hero玩家的筹码变化特别重要，用于盈亏统计
     *
     * 计算逻辑： - stack_after = 行动前筹码 - 本次行动投入金额 - 对于fold/check：筹码数量不变 - 对于call/bet/raise/allin：筹码相应减少
     *
     * 业务规则： - 不能为负数（筹码不能透支） - 全进(allin)后应该为0或接近0 - 必须与行动金额和行动前筹码保持数学一致性
     *
     * 分析应用： 1. 资金管理分析： - 筹码管理效率 - 风险控制能力 - 筹码保护意识
     *
     * 2. 游戏阶段分析： - 深筹码 vs 浅筹码的策略差异 - 筹码压力对决策的影响 - 筹码波动的心理影响
     *
     * 3. Hero统计： - 每手牌的净盈亏计算 - 筹码增长曲线分析 - 风险调整后的收益率
     *
     * 数据验证： - 必须与amount字段保持逻辑一致性 - 不能超过玩家的起始筹码 - allin行动后必须为0或极小值
     *
     * 数据约束：BigDecimal类型，非负数，精确到小数点后2位
     */
    private BigDecimal stackAfter;

    /**
     * 行动后底池 - 执行行动后的底池总金额
     *
     * 业务含义： - 当前行动执行后的底池总金额 - 反映所有玩家的累计投入 - 用于计算奖池大小、底池赔率、获胜价值 - 系统状态同步的重要指标
     *
     * 计算规则： - pot_after = 原底池 + 本次行动金额 - fold/check行动：底池不变 - bet/raise/call/allin行动：底池增加
     *
     * 德扑核心概念： 1. 底池赔率 (Pot Odds)： - 计算公式：需要跟注的金额 / (底池 + 需要跟注的金额) - 用于决策是否跟注的数学基础 - 与手牌胜率对比做出最优决策
     *
     * 2. 隐含赔率 (Implied Odds)： - 考虑后续街道可能获得的额外收益 - 基于当前底池大小评估未来潜在价值 - 影响边际手牌的决策
     *
     * 3. 底池控制： - 通过控制下注大小管理底池增长 - 避免底池过大导致的高风险决策 - 根据手牌强度调整底池构建策略
     *
     * 业务应用： 1. 实时决策支持： - 为AI分析提供底池赔率计算 - 评估当前行动的数学期望 - 支持最优策略推荐
     *
     * 2. 历史分析： - 分析玩家的底池构建习惯 - 评估价值提取效率 - 研究底池大小对决策的影响
     *
     * 3. 游戏重建： - 精确重现每个时点的底池状态 - 支持教学和复盘分析 - 验证游戏过程的正确性
     *
     * 数据验证： - 只能增加，不能减少（除非系统错误修正） - 必须与所有玩家投入的总金额一致 - 最终底池应等于winner获得的金额（扣除rake）
     *
     * 数据约束：BigDecimal类型，非负数，单调递增
     */
    private BigDecimal potAfter;

    /**
     * 思考时间 - 玩家做出决策的耗时（秒）
     *
     * 业务含义： - 玩家从面临决策到执行行动的时间间隔 - 反映决策复杂度和玩家思考深度 - 用于分析玩家的决策模式和心理状态 - AI行为分析和玩家画像的重要特征
     *
     * 德扑心理学意义： 1. 快速决策 (0-3秒)： - 通常表示明显的强牌或弱牌 - 反映玩家对当前局面的确定性 - 可能是基于经验的直觉判断 - 有时是故意展示的"自信"信号
     *
     * 2. 中等思考 (4-10秒)： - 表示需要计算和权衡的中等强度手牌 - 反映玩家在进行数学计算或策略思考 - 可能在考虑对手的可能手牌范围 - 常见于正常的策略决策过程
     *
     * 3. 长时间思考 (10+秒)： - 表示边际牌或复杂的决策环境 - 可能是困难的数学计算或心理博弈 - 反映玩家面临重要的资金决策 - 有时是故意的"演技"或时间消耗策略
     *
     * 行为模式分析： 1. 一致性分析： - 强牌时的思考时间模式 - 弱牌时的思考时间模式 - 识别玩家的"时间tell"
     *
     * 2. 压力反应： - 大额决策时的思考时间变化 - 不同街道的思考时间分布 - 面对加注时的反应时间
     *
     * 3. 经验水平： - 新手通常思考时间更长且更不一致 - 老手的思考时间更加稳定和有目的性 - 专业玩家可能故意控制思考时间
     *
     * AI训练价值： - 作为神经网络的输入特征 - 结合其他特征进行玩家建模 - 预测玩家的手牌强度和策略意图 - 识别玩家的心理状态变化
     *
     * 数据收集注意事项： - 需要考虑网络延迟的影响 - 区分主动思考和被动等待 - 记录系统响应时间以便校正
     *
     * 数据约束：Integer类型，正整数，单位为秒
     */
    private Integer thinkTimeSeconds;

    /**
     * 行动时间戳 - 行动发生的精确时间
     *
     * 业务含义： - 记录行动发生的精确时间点 - 用于计算行动间隔和游戏节奏 - 支持时序分析和游戏重建 - 提供审计跟踪和数据验证基础
     *
     * 业务应用： 1. 游戏节奏分析： - 计算手牌总时长 - 分析不同街道的时间分配 - 识别游戏的快慢节奏偏好
     *
     * 2. 时序验证： - 确保行动顺序的正确性 - 检测异常的时间间隔 - 验证并发行动的处理
     *
     * 3. 用户体验分析： - 分析用户的活跃时间段 - 计算平均等待时间 - 优化游戏流程和响应速度
     *
     * 4. 数据质量保证： - 提供数据完整性验证 - 支持异常数据的识别和修复 - 建立可靠的时间基准
     *
     * 数据约束：Long类型时间戳，精确到毫秒，自动生成
     */
    private Long createTime;
    
    /**
     * 行动更新时间戳(毫秒) - 记录最后修改时间
     */
    private Long updateTime;

    // ================================
    // 德州扑克行动类型常量定义
    // ================================

    /** 弃牌 - 放弃当前手牌，退出游戏 */
    public static final String ACTION_FOLD = "fold";

    /** 过牌 - 不下注但保持游戏，传递行动权 */
    public static final String ACTION_CHECK = "check";

    /** 跟注 - 跟上当前需要的下注金额 */
    public static final String ACTION_CALL = "call";

    /** 下注 - 在无人下注时主动投入筹码 */
    public static final String ACTION_BET = "bet";

    /** 加注 - 在有人下注基础上增加投注 */
    public static final String ACTION_RAISE = "raise";

    /** 全进 - 投入所有剩余筹码 */
    public static final String ACTION_ALLIN = "allin";

    /** 小盲投注 - 强制投注，手牌开始时自动执行 */
    public static final String ACTION_SMALL_BLIND = "small_blind";

    /** 大盲投注 - 强制投注，手牌开始时自动执行 */
    public static final String ACTION_BIG_BLIND = "big_blind";

    // ================================
    // 德州扑克街道常量定义
    // ================================

    /** 翻前 - 只有底牌的阶段 */
    public static final String STREET_PREFLOP = "preflop";

    /** 翻牌 - 3张公共牌的阶段 */
    public static final String STREET_FLOP = "flop";

    /** 转牌 - 4张公共牌的阶段 */
    public static final String STREET_TURN = "turn";

    /** 河牌 - 5张公共牌的阶段 */
    public static final String STREET_RIVER = "river";

    // ================================
    // 构造函数
    // ================================

    /**
     * 默认构造函数 初始化默认值和时间戳
     */
    public PokerHandAction() {
        this.amount = BigDecimal.ZERO;
        this.createTime = System.currentTimeMillis();
        this.updateTime = System.currentTimeMillis();
    }

    /**
     * 业务构造函数 - 创建完整的行动记录
     *
     * @param handId         手牌ID
     * @param seatNumber     座位号
     * @param playerId       前端玩家ID（业务标识符）
     * @param userId         数据库用户ID（性能查询）
     * @param street         街道
     * @param actionSequence 街道内序号
     * @param globalSequence 全局序号
     * @param actionType     行动类型
     */
    public PokerHandAction(Long handId, Integer seatNumber, String playerId, Long userId, String street,
        Integer actionSequence, Integer globalSequence, String actionType) {
        this();
        this.handId = handId;
        this.seatNumber = seatNumber;
        this.playerId = playerId;
        this.userId = userId;
        this.street = street;
        this.actionSequence = actionSequence;
        this.globalSequence = globalSequence;
        this.actionType = actionType;
    }

    // ================================
    // Getters and Setters
    // ================================

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getHandId() {
        return handId;
    }

    public void setHandId(Long handId) {
        this.handId = handId;
    }

    public Integer getSeatNumber() {
        return seatNumber;
    }

    public void setSeatNumber(Integer seatNumber) {
        this.seatNumber = seatNumber;
    }

    public String getPlayerId() {
        return playerId;
    }

    public void setPlayerId(String playerId) {
        this.playerId = playerId;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public Integer getActionSequence() {
        return actionSequence;
    }

    public void setActionSequence(Integer actionSequence) {
        this.actionSequence = actionSequence;
    }

    public Integer getGlobalSequence() {
        return globalSequence;
    }

    public void setGlobalSequence(Integer globalSequence) {
        this.globalSequence = globalSequence;
    }

    public String getActionType() {
        return actionType;
    }

    public void setActionType(String actionType) {
        this.actionType = actionType;
    }

    public BigDecimal getAmount() {
        return amount;
    }

    public void setAmount(BigDecimal amount) {
        this.amount = amount;
    }

    public BigDecimal getStackAfter() {
        return stackAfter;
    }

    public void setStackAfter(BigDecimal stackAfter) {
        this.stackAfter = stackAfter;
    }

    public BigDecimal getPotAfter() {
        return potAfter;
    }

    public void setPotAfter(BigDecimal potAfter) {
        this.potAfter = potAfter;
    }

    public Integer getThinkTimeSeconds() {
        return thinkTimeSeconds;
    }

    public void setThinkTimeSeconds(Integer thinkTimeSeconds) {
        this.thinkTimeSeconds = thinkTimeSeconds;
    }

    public Long getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Long createTime) {
        this.createTime = createTime;
    }
    
    public Long getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(Long updateTime) {
        this.updateTime = updateTime;
    }

    // ================================
    // 德州扑克专业业务方法
    // ================================

    /**
     * 检查是否为主动行动（下注、加注、全进） 主动行动通常表示手牌强度或欺骗意图
     *
     * @return true表示主动行动
     */
    public boolean isAggressiveAction() {
        return ACTION_BET.equals(actionType) || ACTION_RAISE.equals(actionType) || ACTION_ALLIN.equals(actionType);
    }

    /**
     * 检查是否为被动行动（跟注、过牌） 被动行动通常表示相对保守的策略
     *
     * @return true表示被动行动
     */
    public boolean isPassiveAction() {
        return ACTION_CALL.equals(actionType) || ACTION_CHECK.equals(actionType);
    }

    /**
     * 检查是否为弃牌行动 弃牌表示完全退出当前手牌
     *
     * @return true表示弃牌
     */
    public boolean isFold() {
        return ACTION_FOLD.equals(actionType);
    }

    /**
     * 检查是否涉及筹码投入 除了fold和check，其他行动都涉及筹码
     *
     * @return true表示涉及筹码投入
     */
    public boolean involvesAmount() {
        return !ACTION_FOLD.equals(actionType) && !ACTION_CHECK.equals(actionType);
    }

    /**
     * 检查是否为盲注行动 盲注是强制性的投注，在手牌开始时自动执行
     *
     * @return true表示盲注行动
     */
    public boolean isBlindAction() {
        return ACTION_SMALL_BLIND.equals(actionType) || ACTION_BIG_BLIND.equals(actionType);
    }

    /**
     * 检查是否为全进行动 全进表示投入所有剩余筹码
     *
     * @return true表示全进
     */
    public boolean isAllIn() {
        return ACTION_ALLIN.equals(actionType);
    }

    /**
     * 检查是否为翻前行动 翻前行动具有特殊的策略意义
     *
     * @return true表示翻前行动
     */
    public boolean isPreflopAction() {
        return STREET_PREFLOP.equals(street);
    }

    /**
     * 检查是否为翻后行动 翻后行动有更多信息可以利用
     *
     * @return true表示翻后行动
     */
    public boolean isPostflopAction() {
        return STREET_FLOP.equals(street) || STREET_TURN.equals(street) || STREET_RIVER.equals(street);
    }

    /**
     * 获取行动的描述字符串 用于日志记录和界面显示
     *
     * @return 行动的描述
     */
    public String getActionDescription() {
        if (ACTION_FOLD.equals(actionType) || ACTION_CHECK.equals(actionType)) {
            return actionType;
        }
        if (amount != null && amount.compareTo(BigDecimal.ZERO) > 0) {
            return actionType + " " + amount;
        }
        return actionType;
    }

    /**
     * 获取投入筹码占剩余筹码的比例 用于分析下注大小的相对性
     *
     * @return 投入比例 (0.0 - 1.0)，如果无法计算则返回null
     */
    public Double getInvestmentRatio() {
        if (!involvesAmount() || amount == null || stackAfter == null) {
            return null;
        }

        BigDecimal totalStack = stackAfter.add(amount);
        if (totalStack.compareTo(BigDecimal.ZERO) == 0) {
            return null;
        }

        return amount.divide(totalStack, 4, java.math.RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 检查是否为快速决策 思考时间少于3秒被认为是快速决策
     *
     * @return true表示快速决策
     */
    public boolean isQuickDecision() {
        return thinkTimeSeconds != null && thinkTimeSeconds <= 3;
    }

    /**
     * 检查是否为长时间思考 思考时间超过10秒被认为是长时间思考
     *
     * @return true表示长时间思考
     */
    public boolean isLongThink() {
        return thinkTimeSeconds != null && thinkTimeSeconds >= 10;
    }

    // ================================
    // Object 方法重写
    // ================================

    @Override
    public String toString() {
        return "PokerHandAction{" + "id=" + id + ", handId=" + handId + ", seatNumber=" + seatNumber + ", playerId='" + playerId + '\'' + ", userId=" + userId + ", street='" + street + '\'' + ", actionType='" + actionType + '\'' + ", amount=" + amount + ", globalSequence=" + globalSequence + '}';
    }
}